//
//  DailyForecastTableViewCell.swift
//  SwiftWeather
//
//  Created by shen on 2023/8/28.
//

import UIKit
import Combine

final class DailyForecastCellViewModel {
    // MARK: Properties

    @Published private var forecastDay: ForecastDay?
    @Published private var temperatureUnits: TemperatureUnits

    var temperaturesPublisher: AnyPublisher<(low: Temperature, high: Temperature), Never> {
        lowTemperaturePublisher.combineLatest(highTemperaturePublisher)
            .map { (low: $0, high: $1) }
            .eraseToAnyPublisher()
    }

    var weatherCodePublisher: AnyPublisher<Int, Never> {
        $forecastDay
            .compactMap { $0?.day?.condition?.code }
            .eraseToAnyPublisher()
    }

    var weekdayPublisher: AnyPublisher<String, Never> {
        $forecastDay
            .compactMap { $0?.date?.convertToDate(format: DateFormat.dailyWeatherForecast.rawValue) }
            .map { $0.toString(format: DateFormat.weekday.rawValue) }
            .eraseToAnyPublisher()
    }

    private var lowTemperaturePublisher: AnyPublisher<Temperature, Never> {
        $forecastDay.combineLatest($temperatureUnits)
            .compactMap { forecastDay, units in
                guard let day = forecastDay?.day else { return nil }
                return (day, units)
            }
            .flatMap {
                Just($0).mapToTemperature(type: .low, in: $1)
            }
            .eraseToAnyPublisher()
    }

    private var highTemperaturePublisher: AnyPublisher<Temperature, Never> {
        $forecastDay.combineLatest($temperatureUnits)
            .compactMap { forecastDay, units in
                guard let day = forecastDay?.day else { return nil }
                return (day, units)
            }
            .flatMap {
                Just($0).mapToTemperature(type: .high, in: $1)
            }
            .eraseToAnyPublisher()
    }

    // MARK: - Initialization

    init(forecastDay: ForecastDay? = nil, temperatureUnits: TemperatureUnits) {
        self.forecastDay = forecastDay
        self.temperatureUnits = temperatureUnits
    }
}

class DailyForecastTableViewCell: BaseTableViewCell, ViewModelable {
    typealias ViewModel = DailyForecastCellViewModel

    // MARK: Properties

    var viewModel: ViewModel! {
        didSet {
            viewModel.temperaturesPublisher
                .sink { [weak self] lowTemperature, highTemperature in
                    self?.lowTemperatureLabel.text = lowTemperature.stringFormat
                    self?.highTemperatureLabel.text = highTemperature.stringFormat
                }
                .store(in: &cancellables)

            viewModel.weekdayPublisher
                .assignToTextOnLabel(weekdayLabel)
                .store(in: &cancellables)

            viewModel.weatherCodePublisher
                .sink { [weak self] code in
                    self?.weatherIconImageView.image = Resources.Images.Weather.weatherIcon(
                        code: code,
                        dayPriod: .day
                    )
                }
                .store(in: &cancellables)
        }
    }

    private var cancellables = Set<AnyCancellable>()

    override func prepareForReuse() {
        super.prepareForReuse()
        changeLabelsColor(to: .label)
    }

    // MARK: - Views

    private lazy var mainHStack = UIStackView(
        arrangedSubviews: [weekdayLabel, weatherIconImageView, temperaturesHStack],
        axis: .horizontal,
        spacing: 10,
        alignment: .center,
        distribution: .equalSpacing
    )

    private lazy var weekdayLabel = UILabel(font: Resources.Fonts.system(weight: .semibold))

    private lazy var weatherIconImageView = UIImageView(contentMode: .scaleAspectFit)

    private lazy var temperaturesHStack = UIStackView(
        arrangedSubviews: [lowTemperatureLabel, highTemperatureLabel],
        axis: .horizontal,
        spacing: 20,
        distribution: .fillEqually
    )

    private lazy var lowTemperatureLabel = UILabel(
        font: Resources.Fonts.system(weight: .semibold),
        textAlignment: .center
    )

    private lazy var highTemperatureLabel = UILabel(
        font: Resources.Fonts.system(weight: .semibold),
        textAlignment: .center
    )

    // MARK: - Methods

    func changeLabelsColor(to color: UIColor) {
        [weekdayLabel, lowTemperatureLabel, highTemperatureLabel].forEach { label in
            label.textColor = color
        }
    }

    override func configureAppearance() {
        backgroundColor = .clear
    }

    override func setupViews() {
        lowTemperatureLabel.alpha = 0.6
        contentView.addSubview(mainHStack, useAutoLayout: true)
    }

    override func constraintViews() {
        NSLayoutConstraint.activate([
            lowTemperatureLabel.widthAnchor.constraint(equalToConstant: 50),
            highTemperatureLabel.widthAnchor.constraint(equalToConstant: 50),

            weekdayLabel.widthAnchor.constraint(equalToConstant: 100),

            mainHStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            mainHStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
            mainHStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
            mainHStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20)
        ])
    }
}
